<template>
  <div id="app">
    <div class="box">
      <h3>钱包连接 <el-tag :type="account===''?'danger':'success'" effect="plain" size="small">
          {{ account ==='' ? '未连接':'已连接' }} </el-tag>
      </h3>
      <el-button type="primary" @click="handleConnect">连接mateMask钱包</el-button>
      <el-button type="primary" @click="handleDisconnect" :disabled="disabled">断开钱包连接</el-button>
      <el-button type="primary" @click="handleChangeNetwork" :disabled="disabled">切换链</el-button>
      <el-button type="primary" @click="handleAddNetwork">添加BSC链</el-button>
      <el-divider></el-divider>
      <el-descriptions class="margin-top" title="钱包账户信息" direction="vertical" :column="2" border>
        <el-descriptions-item label="钱包地址"><a :href="`${block_url}/address/${account}`" target="_blank">{{account}}</a>
        </el-descriptions-item>
        <el-descriptions-item label="链ID">{{chainId}}</el-descriptions-item>
        <el-descriptions-item label="ETH余额">{{balance}}</el-descriptions-item>
      </el-descriptions>
      <el-divider></el-divider>
      <h3>合约交互-- 获取链上信息</h3>
      <div v-loading="loadingCall">
        <el-row :gutter="10">
          <el-col :span="18">
            <el-input v-model="erc_address" placeholder="请输入ERC20代币地址">
              <el-button slot="append" @click="erc_address = erc_contract">NGK代币</el-button>
            </el-input>
          </el-col>
          <el-col :span="6">
            <el-button type="primary" @click="handleCall" :disabled="disabled">获取代币信息</el-button>
          </el-col>
        </el-row>
        <el-divider></el-divider>
        <el-descriptions class="margin-top" title="代币信息" direction="vertical" :column="4" border>
          <el-descriptions-item label="代币Name">{{coin.name}}</el-descriptions-item>
          <el-descriptions-item label="代币余额">{{coin.balance}}</el-descriptions-item>
          <el-descriptions-item label="代币Symbol">{{coin.symbol}}</el-descriptions-item>
          <el-descriptions-item label="代币Decimal">{{coin.decimals}}</el-descriptions-item>
          <el-descriptions-item label="代币Contract"><a :href="`${block_url}/token/${coin.contract}`"
              target="_blank">{{coin.contract}}</a></el-descriptions-item>
        </el-descriptions>
      </div>
      <el-divider></el-divider>
      <h3>合约交互-- 发起链上交易</h3>
      <div v-loading="loadingTx">
        <el-row :gutter="10" style="margin-bottom: 15px;">
          <el-col :span="18">
            <el-input v-model="nft_address" placeholder="请输入ERC721地址">
              <el-button slot="append" @click="nft_address = nft_contract">Godex合约</el-button>
            </el-input>
          </el-col>
          <el-col :span="6">
            <el-input v-model="tokenId" placeholder="TokenID">
            </el-input>
          </el-col>
        </el-row>
        <el-row :gutter="10">
          <el-col :span="18">
            <el-input v-model="toAddress" placeholder="请输入转赠钱包地址">
            </el-input>
          </el-col>
          <el-col :span="6">
            <el-button style="width: 100%;" type="primary" @click="handleTransaction" :disabled="disabled">发起交易
            </el-button>
          </el-col>
        </el-row>
        <div v-if="txHash!=''" style="margin-top: 15px;background-color: #f0f9eb;padding:8px;text-align:left;">
          <el-row :gutter="10">
            <el-col :span="22">
              <span>查看交易详情: </span><a :href="`${block_url}/tx/${txHash}`" target="_blank">{{`${txHash}`}}</a>
            </el-col>
            <el-col :span="2" style="text-align:right; font-size: 28px;">
              <span v-if="txStatus === 'pending'" class="el-icon-loading"></span>
              <span v-if="txStatus === 'complete'" class="el-icon-circle-check"></span>
              <span v-if="txStatus === 'fail'" class="el-icon-circle-close"></span>
            </el-col>
          </el-row>
        </div>

      </div>

    </div>
  </div>
</template>

<script>
import erc20ABI from '@/abi/erc20.json'
import erc721ABI from '@/abi/erc721.json'

import connectMetaMask from "@/wallet/metamask";
import { utils, Contract, BigNumber } from "ethers";
import { Message } from 'element-ui'

export default {
  name: 'App',
  data () {
    return {
      block_url: 'https://testnet.bscscan.com', // BSC区块浏览器

      // 账户信息
      provider: '', //区块链的连接实例
      account: '',
      balance: '',
      chainId: '',

      // 代币信息
      erc_address: '',
      erc_contract: '0x4834e69dFa970B8789cBb887581CbE0C8e6C364e', // NKG 代币
      coin: {
        name: '',
        balance: '',
        symbol: '',
        decimals: '',
        contract: '',
      },

      // NFT信息
      nft_address: '',
      nft_contract: '0x64517F7669FE43156D0d2e3975eEC74D8a6C2E96',
      toAddress: '', // 0xF3540E1a1327d9BA911B788CE64CA205f8Dcd4A6
      tokenId: '',
      txHash: '', //交易hash
      txStatus: '',

      loadingCall: false,
      loadingTx: false,

    }
  },
  computed: {
    disabled () {
      return this.account === ''
    }
  },
  created () {

  },
  mounted () {
    const walletType = localStorage.getItem('walletType')
    if (walletType === 'MateMask') {
      this.handleConnect()
    }
  },
  methods: {
    // 连接钱包
    async handleConnect () {
      try {
        const obj = await connectMetaMask();
        if (!obj) {
          localStorage.removeItem("walletType");
          localStorage.removeItem("chainId");
          return;
        }

        localStorage.setItem("walletType", 'MateMask');
        localStorage.setItem("chainId", obj.chainId);

        const { account, provider, chainId } = obj
        this.provider = provider
        this.account = account
        this.chainId = chainId

        // 获取当前钱包地址的主币余额
        const balance = await provider.getBalance(account)
        this.balance = utils.formatUnits(balance.toString(), 18).toString();

      } catch (error) {
        console.log('[ error ] >', error)
      }
    },
    // 断开连接
    handleDisconnect () {
      localStorage.removeItem("walletType");
      localStorage.removeItem("chainId");

      setTimeout(() => {
        location.reload()
      }, 500)
    },
    // 获取链上信息Call方法
    async handleCall () {
      if (!utils.isAddress(this.erc_address)) {
        this.$notify({
          title: '警告',
          message: 'ERC20地址错误，请输入正确的代币地址',
          type: 'warning',
          onClose: () => {
            this.erc_address = ''
          }
        });
        return
      }
      this.loadingCall = true;
      const coinContract = new Contract(this.erc_address, erc20ABI, this.provider)
      try {
        const decimals = await coinContract.decimals()
        const name = await coinContract.name()
        const symbol = await coinContract.symbol()

        const balance = await coinContract.balanceOf(this.account)

        this.coin.name = name
        this.coin.contract = this.erc_address
        this.coin.symbol = symbol
        this.coin.decimals = decimals
        this.coin.balance = utils.formatUnits(balance.toString(), decimals).toString();

        this.loadingCall = false;
      } catch (error) {
        console.log('[ error ] >', error)
        this.$notify({
          title: '警告',
          message: '该地址不是标准ERC20合约地址',
          type: 'warning',
        });
      }

      this.loadingCall = false;
    },
    // 发起交易
    async handleTransaction () {
      const from = this.account
      const to = this.toAddress
      const tokenId = this.tokenId
      if (!utils.isAddress(to) || isNaN(tokenId) || !utils.isAddress(this.nft_address)) {
        this.$notify({
          title: '警告',
          message: 'NFT 合约地址错误或者TokenID格式错误',
          type: 'warning',
          onClose: () => {
            this.erc_address = ''
          }
        });
        return
      }
      // 判断输入的TokenId是否属于当前地址, ownerOf合约方法
      try {
        const coinContract = new Contract(this.nft_address, erc721ABI, this.provider)
        const res = await coinContract.ownerOf(tokenId)
        if (res.toLocaleLowerCase() !== from) {
          this.$notify({
            title: '警告',
            message: `TokenId: ${tokenId} 不属于当前钱包`,
            type: 'warning',
          });
          return
        }
      } catch (error) {
        console.log('[ error ] >', error)
      }
      const iface = new utils.Interface(erc721ABI)
      const data = iface.encodeFunctionData('transferFrom', [from, to, tokenId])

      this.loadingTx = true
      let gas = BigNumber.from('10000')
      try {
        // 预执行，计算gas费
        gas = await this.provider.estimateGas({
          from: from,
          to: this.nft_address,
          data: data,
          value: 0
        })
      } catch (error) {
        console.log('[ error ] >', error)
        const message = error.data ? error.data.message : error.error.message
        Message.warning(message)
      }

      // 发起交易
      try {
        const signer = this.provider.getSigner()
        const tx = await signer.sendTransaction({
          to: this.nft_address,
          data: data,
          value: 0,
          gasLimit: gas.toNumber()
        })
        this.txHash = tx.hash
        this.$message.success('交易已发送链上，请等待交易完成！')
        this.loadingTx = false

        this.txStatus = 'pending'
        await tx.wait(1) // 监听链上交易是否打包完成
        console.log('[ 交易完成 ] >')
        this.txStatus = 'complete'
      } catch (error) {
        this.txStatus = 'fail'
        console.log('[ error ] >', error)
      }
    },
    // 添加网络
    async handleAddNetwork () {
      if(this.chainId === 97){
        return
      }
      const BSC_Network = {
        chainId: 97,
        blockExplorerUrls: 'https://testnet.bscscan.com',
        name: 'BSC',
        rpcUrls: 'https://speedy-nodes-nyc.moralis.io/cd647532dcdc3ae204954045/bsc/testnet',
        symbol: 'BNB',
        decimals: 18,
        logoURI: 'https://ipfs.io/ipfs/bafybeiedsdius6xplwrft3qbdmy5e7oxibwhc6s7znrvo2zhbtez7qkchq/wBNB.svg',
      }
      const {
        chainId,
        blockExplorerUrls,
        name,
        rpcUrls,
        symbol,
        decimals,
      } = BSC_Network
      if (window.ethereum && window.ethereum.isMetaMask) {
        try {
          await window.ethereum.request({
            method: "wallet_addEthereumChain",
            params: [
              {
                chainId: BigNumber.from(chainId)
                  .toHexString()
                  .replace("0x0", "0x"),
                chainName: name,
                nativeCurrency: {
                  name: symbol,
                  symbol: symbol,
                  decimals: decimals,
                },
                rpcUrls: [rpcUrls],
                blockExplorerUrls: [blockExplorerUrls],
              },
            ],
          });
        } catch (error) {
          console.log("[ error ] >", error);
        }
      }
    },
    // 切换链
    async handleChangeNetwork () {
      const chainId = this.chainId === 97 ? 4 : 97
      if (window.ethereum && window.ethereum.isMetaMask) {
        try {
          await window.ethereum.request({
            method: "wallet_switchEthereumChain",
            params: [
              {
                chainId: BigNumber.from(chainId)
                  .toHexString()
                  .replace("0x0", "0x"),
              },
            ],
          });
        } catch (error) {
          console.log("[ error ] >", error);
        }
      }
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.box {
  width: 700px;
  margin: 0 auto;
}
</style>
